﻿// <copyright file="WebAnalyticsService.cs" company="Microsoft Corporation">
// Copyright (c) 2009 Microsoft Corporation All Rights Reserved
// </copyright>
// <author>Michael S. Scherotter</author>
// <email>mischero@microsoft.com</email>
// <date>2009-10-08</date>
// <summary>WebAnalytics Service class definition</summary>

namespace Microsoft.WebAnalytics
{
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel.Composition;
    using System.Diagnostics.CodeAnalysis;
    using System.IO.IsolatedStorage;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Interactivity;
#if WINDOWS_PHONE
    using Microsoft.Phone.Controls;
#endif
    using Microsoft.WebAnalytics.Contracts;

    /// <summary>
    /// Web Analytics Service for Silverlight Applications
    /// </summary>
    public class WebAnalyticsService : IApplicationService, IApplicationLifetimeAware
    {
        #region Fields

        /// <summary>
        /// The default layoutRoot name for the 
        /// </summary>
        private const string DefaultLayoutRootName = "LayoutRoot";

        /// <summary>
        /// The single web analytics service
        /// </summary>
        private static WebAnalyticsService service;

        /// <summary>
        /// The popup that the service parent gets added to
        /// </summary>
        private Popup popup;

        /// <summary>
        /// The service parent
        /// </summary>
        private Grid serviceParent;

        /// <summary>
        /// true if the service behaviors have been added to the LayoutRoot element
        /// </summary>
        private bool behaviorsAdded;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the WebAnalyticsService class.
        /// </summary>
        public WebAnalyticsService()
        {
            this.LayoutRootName = DefaultLayoutRootName;

            this.Services = new Collection<System.Windows.Interactivity.Behavior>();

            this.IsPageTrackingEnabled = true;

            this.AreApplicationLifetimeEventsEnabled = true;
        }
        #endregion

        /// <summary>
        /// Gets the singleton WebAnalyticsService
        /// </summary>
        public static WebAnalyticsService Current
        {
            get
            {
                if (service != null)
                {
                    return service;
                }

                service = Application.Current.ApplicationLifetimeObjects.OfType<WebAnalyticsService>().FirstOrDefault();

                if (service == null)
                {
                    service = new WebAnalyticsService();

                    service.StartService(null);
                }

                return service;
            }
        }

        /// <summary>
        /// Gets or sets the Log method
        /// </summary>
        /// <remarks>Imported from MEF</remarks>
        [Import("Log")]
        public Action<AnalyticsEvent> Log { get; set; }
        
        /// <summary>
        /// Gets or sets the name of the root visual panel (Typically LayoutRoot)
        /// </summary>
        /// <remarks>You set the LayoutRootName when you want to specify the 
        /// element to add the service behaviors to when they are declared in 
        /// the Services collection.</remarks>
        public string LayoutRootName { get; set; }

        #region Properties

        /// <summary>
        /// Gets or sets the service behaviors
        /// </summary>
        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Necessary for XAML serialization.")]
        public Collection<System.Windows.Interactivity.Behavior> Services { get; set; }

        /// <summary>
        /// Gets or sets the event log
        /// </summary>
        [Import]
        public EventLog EventLog { get; set; }

        /// <summary>
        /// Gets or sets the data collector
        /// </summary>
        [Import]
        public IDataCollector DataCollector { get; set; }

        /// <summary>
        /// Gets or sets the events that each dispatcher can hook into
        /// </summary>
        [Import]
        public WebAnalyticsEvents Events { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether automatic page tracking is enabled (default is true)
        /// </summary>
        public bool IsPageTrackingEnabled { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether application lifetime events (Activated, Closing, Deactivated, Launching) tracking is enabled (default is true)
        /// </summary>
        public bool AreApplicationLifetimeEventsEnabled { get; set; }

        #endregion

        #region Methods
        /// <summary>
        /// When service behaviors are defined in the WebAnalyticsService.Services collection, they need to be
        /// added to the scene in a persistent Popup
        /// </summary>
        public void AttachServices()
        {
            if (this.behaviorsAdded)
            {
                return;
            }

            if (this.Services.Count == 0)
            {
                return;
            }

            this.popup = new Popup
            {
                HorizontalOffset = 20,
                IsHitTestVisible = false,
                VerticalOffset = 40
            };

            this.serviceParent = new Grid();

            var behaviors = Interaction.GetBehaviors(this.serviceParent);

            foreach (var behavior in this.Services)
            {
                behaviors.Add(behavior);
            }

            //// this.serviceParent.Children.Add(new Rectangle { Width = 100, Height = 100, Fill = new SolidColorBrush(Colors.Red), Margin = new Thickness(10, 40, 0, 0) });
            //// this.serviceParent.Children.Add(new TextBlock { Text = "Analytics", Margin = new Thickness(10, 40, 0, 0) });

            this.popup.Child = this.serviceParent;

            this.popup.IsOpen = true;

            this.behaviorsAdded = true;

            this.popup.Loaded += new RoutedEventHandler(this.OnPopupLoaded);
        }

        #endregion

        #region IApplicationService Members

        /// <summary>
        /// Service is starting
        /// </summary>
        /// <param name="context">the application service context</param>
        public void StartService(ApplicationServiceContext context)
        {
            CompositionInitializerEx.SatisfyImports(this);

            this.AttachServices();

            if (context == null)
            {
                this.DataCollector.Initialize(null);
            }
            else
            {
                this.DataCollector.Initialize(context.ApplicationInitParams);
            }

#if WINDOWS_PHONE
            var service = Microsoft.Phone.Shell.PhoneApplicationService.Current;

            if (service != null && this.AreApplicationLifetimeEventsEnabled)
            {
                service.Activated += new System.EventHandler<Phone.Shell.ActivatedEventArgs>(Current_Activated);
                service.Closing += new System.EventHandler<Phone.Shell.ClosingEventArgs>(service_Closing);
                service.Deactivated += new System.EventHandler<Phone.Shell.DeactivatedEventArgs>(service_Deactivated);
                service.Launching += new System.EventHandler<Phone.Shell.LaunchingEventArgs>(service_Launching);
            }
#endif
        }

#if WINDOWS_PHONE
        void service_Launching(object sender, Phone.Shell.LaunchingEventArgs e)
        {
            var logEvent = new AnalyticsEvent
            {
                Name="Launching",
                ObjectType = sender.GetType().Name
            };

            this.DataCollector.Log(logEvent);
        }

        void service_Deactivated(object sender, Phone.Shell.DeactivatedEventArgs e)
        {
            var logEvent = new AnalyticsEvent
            {
                Name = "Deactivated",
                ObjectType = sender.GetType().Name
            };

            this.DataCollector.Log(logEvent);
        }

        void service_Closing(object sender, Phone.Shell.ClosingEventArgs e)
        {
            var logEvent = new AnalyticsEvent
            {
                Name = "Closing",
                ObjectType = sender.GetType().Name
            };

            this.DataCollector.Log(logEvent);
           
        }

        void Current_Activated(object sender, Phone.Shell.ActivatedEventArgs e)
        {
            var logEvent = new AnalyticsEvent
            {
                Name = "Activated",
                ObjectType = sender.GetType().Name
            };

            this.DataCollector.Log(logEvent);
        }
#endif

        /// <summary>
        /// Service has stopped, so write the event log
        /// </summary>
        public void StopService()
        {
            try
            {
                this.EventLog.WriteLog();
            }
            catch (IsolatedStorageException ise)
            {
                System.Diagnostics.Debug.WriteLine(ise.Message);
            }
        }

        #endregion

        #region IApplicationLifetimeAware Members

        /// <summary>
        /// Service has exited
        /// </summary>
        public void Exited()
        {
            var logEvent = new AnalyticsEvent()
            {
                Name = "Exited",
                ObjectType = this.GetType().Name
            };

            this.DataCollector.Log(logEvent);
        }

        /// <summary>
        /// Service is exiting
        /// </summary>
        public void Exiting()
        {
            var logEvent = new AnalyticsEvent()
            {
                Name = "Exiting",
                ObjectType = this.GetType().Name
            };

            this.DataCollector.Log(logEvent);
        }

        /// <summary>
        /// Service has started
        /// </summary>
        public void Started()
        {
            var logEvent = new AnalyticsEvent()
            {
                Name = "Started",
                ObjectType = this.GetType().Name
            };

            this.DataCollector.Log(logEvent);
        }

        /// <summary>
        /// Service is starting
        /// </summary>
        public void Starting()
        {
            var logEvent = new AnalyticsEvent()
            {
                Name = "Starting",
                ObjectType = this.GetType().Name
            };

            this.DataCollector.Log(logEvent);
        }

        #endregion

        #region Implementation
        /// <summary>
        /// Add the Windows Phone Frame Navigated event handler once the Popup is loaded.
        /// </summary>
        /// <param name="sender">the Popup control</param>
        /// <param name="e">the routed event arguments</param>
        private void OnPopupLoaded(object sender, RoutedEventArgs e)
        {
#if WINDOWS_PHONE
            if (this.IsPageTrackingEnabled)
            {
                var frame = Application.Current.RootVisual as PhoneApplicationFrame;

                frame.Navigated += new System.Windows.Navigation.NavigatedEventHandler(this.OnFrameNavigated);
            }
#endif
        }

        /// <summary>
        /// Root Navigated event handler
        /// </summary>
        /// <param name="sender"> the NavigationService</param>
        /// <param name="e">the navigation event arguments</param>
        private void OnFrameNavigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            if (this.IsPageTrackingEnabled)
            {
                var logEvent = new AnalyticsEvent
                {
                    ActionValue = e.Uri.ToString(),
                    HitType = Data.HitType.PageView,
                    Name = "CurrentScreenChanged",
                    NavigationState = e.Uri.ToString(),
                    ObjectType = sender.GetType().Name
                };

                this.Log(logEvent);
            }
        }

        #endregion
    }
}
